package com.larly.manager.websocket;

import cn.hutool.core.util.ObjUtil;
import cn.hutool.extra.ssh.JschUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.larly.model.domain.PrivateMessage;
import com.larly.model.domain.User;
import com.larly.model.dto.privatemessage.PrivateMessageCreateRequest;
import com.larly.model.vo.PrivateMessageVO;
import com.larly.service.PrivateMessageService;
import com.larly.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PathVariable;

import javax.annotation.Resource;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 私信WebSocket服务
 * 端点：/ws/private-message/{userId}
 */
@ServerEndpoint("/ws/private-message/{userId}")
@Component
@Slf4j
public class PrivateMessageWebSocket {

    // 1. 线程池：处理“异步存库”（避免存数据库的耗时操作阻塞实时推送）
    private static final ExecutorService EXECUTOR_SERVICE = Executors.newFixedThreadPool(10);

    // 2. 存储用户连接：key=用户ID，value=WebSocket会话（Session）
    // 用 ConcurrentHashMap 是因为多用户并发操作时安全（避免线程问题）
    private static final Map<Long, Session> USER_SESSIONS = new ConcurrentHashMap<>();

    // 3. 服务层注入：WebSocket 是多实例的，所以用静态变量存储 Service（否则注入会失败）
    private static PrivateMessageService privateMessageService;
    private static UserService userService;

    // 4. 静态注入的 setter 方法：Spring 会调用这个方法给静态 Service 赋值
    @Resource
    public void setPrivateMessageService(PrivateMessageService service) {
        PrivateMessageWebSocket.privateMessageService = service;
    }

    @Resource
    public void setUserService(UserService service) {
        PrivateMessageWebSocket.userService = service;
    }


    /**
     * 用户连接时触发
     *
     * @param session 用户连接的会话
     * @param userId  用户ID
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("userId") String userId) {
        // 建立连接
        USER_SESSIONS.put(Long.parseLong(userId), session);
        log.info("用户连接：{}", userId);
    }

    /**
     * 接收到消息时触发
     * @param message 消息内容
     * @param senderId  发送者ID
     */
    @OnMessage
    public void onMessage(String message, @PathParam("userId") String senderId) {
        // 步骤1：解析前端传的JSON消息，转成Java对象（PrivateMessageSendDTO）
        // 比如前端发的JSON是 {"receiverId":200,"content":"你好","msgType":1}，这里会转成DTO
        PrivateMessageCreateRequest dto = JSONUtil.toBean(message, PrivateMessageCreateRequest.class);
        Integer receiverId = dto.getReceiverId();// 接收者ID（比如200）

        // 步骤2：校验参数
        if (ObjUtil.isEmpty(receiverId) || ObjUtil.isEmpty(dto.getContent())) {
            log.warn("私信参数无效：senderId={}, message={}", senderId, message);
            return;
        }

        // 步骤3：创建入库（异步，线程池）
        EXECUTOR_SERVICE.submit(() -> {
            privateMessageService.createPrivateMessage(dto, Integer.parseInt(senderId));
        });
//        privateMessageService.createPrivateMessage(dto, Integer.parseInt(senderId));

        // 步骤4：发送消息给接收者
        Session receiverSession = USER_SESSIONS.get(receiverId);
        // 如果接收者已连接
        if (receiverSession != null && receiverSession.isOpen()) {
            try {
                // 构建推送用的VO（给前端返回的格式，包含发送者昵称、头像等，方便前端显示）
                User sender = userService.getById(senderId); // 查发送者的用户信息
                String senderNickname = sender != null ? sender.getNickname() : "匿名用户";
                String senderAvatar = sender != null ? sender.getAvatar() : "";

                PrivateMessageVO pushVO = new PrivateMessageVO();
                pushVO.setSenderId(Integer.parseInt(senderId));         // 发送者ID
                pushVO.setSenderNickname(senderNickname); // 发送者昵称
                pushVO.setSenderAvatar(senderAvatar);   // 发送者头像
                pushVO.setReceiverId(receiverId);      // 接收者ID
                pushVO.setContent(dto.getContent());    // 消息内容
                pushVO.setMsgType(dto.getMsgType());    // 消息类型（1=文本，2=图片）
                pushVO.setIsRead(0);                   // 初始未读
                pushVO.setSendTime(new Date());         // 发送时间

                // 把VO转成JSON，通过接收者的Session推送给前端
                receiverSession.getBasicRemote().sendText(JSONUtil.toJsonStr(pushVO));
                log.info("私信推送成功：senderId={}, receiverId={}", senderId, receiverId);
            } catch (Exception e) {
                log.error("发送私信失败：senderId={}, receiverId={}", senderId, receiverId, e);
            }
        }
    }


    /**
     * 用户关闭页面、断开连接时调用（比如关闭私信页、退出登录）
     * @param userId 断开连接的用户ID
     * @param session 要关闭的会话
     */
    @OnClose
    public void onClose(@PathParam("userId") Long userId, Session session) {
        // 从 USER_SESSIONS 中移除该用户，标记为离线
        USER_SESSIONS.remove(userId);
        try {
            // 关闭会话（释放资源）
            session.close();
        } catch (IOException e) {
            log.error("关闭会话失败：userId={}", userId, e);
        }
        log.info("用户[{}]断开WebSocket连接，当前在线数：{}", userId, USER_SESSIONS.size());
    }

    /**
     * 连接出现异常时调用（比如网络断了、前端崩溃）
     * @param userId 异常用户的ID
     * @param session 异常的会话
     * @param throwable 异常信息
     */
    @OnError
    public void onError(@PathParam("userId") Long userId, Session session, Throwable throwable) {
        log.error("用户[{}]WebSocket连接异常", userId, throwable);
        // 异常时也要移除连接（避免存无效的Session）
        USER_SESSIONS.remove(userId);
        try {
            session.close(); // 关闭异常会话
        } catch (IOException e) {
            log.error("异常关闭会话失败：userId={}", userId, e);
        }
    }

    /**
     * 主动推送消息给用户（比如用户重连后, 推送他离线时收到的未读消息）
     * @param userId 要推送的用户ID
     * @param vo 要推送的私信VO
     */
    public static void pushMessage(Long userId, PrivateMessageVO vo) {
        // 查用户是否在线
        Session session = USER_SESSIONS.get(userId);
        if (session != null && session.isOpen()) {
            try {
                // 推送消息
                session.getBasicRemote().sendText(JSONUtil.toJsonStr(vo));
                log.info("主动推送消息给用户[{}]成功", userId);
            } catch (IOException e) {
                log.error("主动推送消息失败：userId={}", userId, e);
            }
        }
    }

}
